package com.franklin.validation.core.aspect;


import com.franklin.validation.core.RequestParamRuleFactory;
import com.franklin.validation.core.annotations.NotNullForMap;
import com.franklin.validation.core.config.ValidationConfig;
import com.franklin.validation.core.constants.Constants;
import com.franklin.validation.core.entity.CheckJsonEntity;
import com.franklin.validation.core.utils.ReflectionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Description :
 * 使用Aop进行校验避免，WebFlux与WebMVC参数校验配置不统一，
 * 性能比直接实现HandlerMethodArgumentResolver高，
 * 亲测WebFlux实现自定义注解并实现HandlerMethodArgumentResolver解析form data参数，
 * 大概7-8个参数请求就会直接卡死
 * Create By: Franklin
 * Date : 2020/6/1 17:33
 */
@Aspect
@Configuration
public class ControllerAspectForMap {

    @Autowired
    private RequestParamRuleFactory factory;
    @Autowired
    private ValidationConfig validationConfig;

    @Pointcut("@annotation(com.franklin.validation.core.annotations.NotNullForMap)")
    public void mapTarget(){}

    @Around("mapTarget() && @annotation(notNullForMap)")
    public Object mapAround(ProceedingJoinPoint pjp, NotNullForMap notNullForMap){
        Class returnType = null;
        try {
            Class<?> controllerClass = pjp.getTarget().getClass();
            Object[] args = pjp.getArgs();
            List<? extends Class<?>> classList = Arrays.stream(args)
                    .map(Object::getClass)
                    .collect(Collectors.toList());
            Class[] parameterTypes = new Class[classList.size()];
            IntStream.range(0,classList.size())
                    .forEach(i -> parameterTypes[i] = ReflectionUtil.getInterfaces(classList.get(i)));
            Method declaredMethod = controllerClass.getDeclaredMethod(pjp.getSignature().getName(), parameterTypes);
            returnType = declaredMethod.getReturnType();
            Annotation[][] annotations = declaredMethod.getParameterAnnotations();
            AtomicInteger index = new AtomicInteger(-1);
            IntStream.range(0,annotations.length)
                    .forEach(i ->  Arrays.stream(annotations[i])
                                .forEach(annotation -> {
                                    if (annotation.annotationType().isAssignableFrom(RequestBody.class)){
                                        index.set(i);
                                    }
                                }));
            int i = index.get();
            if (i > -1){
                Map<String,Object> params = (Map<String,Object>)args[i];
                CheckJsonEntity checkJsonEntity = new CheckJsonEntity(params, notNullForMap);
                String check = this.factory.check(checkJsonEntity);
                if (Constants.SUCCESS_STR.equals(check)){
                    return pjp.proceed(args);
                }
                String msg;
                if (check.startsWith(Constants.MESSAGE)){
                    msg = check.split(":")[1];
                }else {
                    msg = String.format(this.validationConfig.getMessage(),check);
                }
                return this.factory.getResponse(this.validationConfig.getCode() + Constants.COMMA + msg,returnType);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        if(Objects.nonNull(returnType)){
            try {
                return this.factory.defaultResponse(returnType);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new Object();
    }
}
